package com.bstek.uflo.console.handler.impl.designer;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert;

import com.bstek.uflo.command.CommandService;
import com.bstek.uflo.command.impl.GetProcessCommand;
import com.bstek.uflo.console.handler.impl.PageData;
import com.bstek.uflo.console.handler.impl.RenderPageServletHandler;
import com.bstek.uflo.console.provider.ProcessFile;
import com.bstek.uflo.console.provider.ProcessProvider;
import com.bstek.uflo.console.provider.ProcessProviderUtils;
import com.bstek.uflo.deploy.impl.DefaultProcessDeployer;
import com.bstek.uflo.deploy.parse.impl.ProcessParser;
import com.bstek.uflo.env.AdditionalInfoProvider;
import com.bstek.uflo.model.ProcessDefinition;
import com.bstek.uflo.query.ProcessQuery;
import com.bstek.uflo.service.ProcessService;
import com.bstek.uflo.service.impl.DefaultProcessService;
import com.bstek.uflo.utils.IDGenerator;

/**
 * @author Jacky.gao
 * @since 2017年7月5日
 */
public class DesignerServletHandler extends RenderPageServletHandler {
	private ProcessService processService;
	private AdditionalInfoProvider additionalInfoProvider;
	private CommandService commandService;
	private DefaultProcessService defaultProcessService;
	private DefaultProcessDeployer defaultProcessDeployer;

	@Override
	public void execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String method = retriveMethod(req);
		if (method != null) {
			invokeMethod(method, req, resp);
		} else {
			VelocityContext context = new VelocityContext();
			context.put("contextPath", req.getContextPath());
			if (additionalInfoProvider != null) {
				List<String> categories = additionalInfoProvider.categories();
				if (categories != null && categories.size() > 0) {
					StringBuilder sb = new StringBuilder();
					for (String category : categories) {
						if (sb.length() > 0) {
							sb.append(",");
						}
						sb.append("\"" + category + "\"");
					}
					context.put("categories", sb.toString());
				}
			} else {
				context.put("categories", "");
			}
			resp.setContentType("text/html");
			resp.setCharacterEncoding("utf-8");
			Template template = ve.getTemplate("uflo-html/designer.html", "utf-8");
			PrintWriter writer = resp.getWriter();
			template.merge(context, writer);
			writer.close();
		}
	}

	public void deploy(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String content = req.getParameter("content");
		content = decode(content);
		InputStream inputStream = null;
		try {
			inputStream = IOUtils.toInputStream(content, "utf-8");
			processService.deployProcess(inputStream);
			writeObjectToJson(resp, HttpServletResponse.SC_OK);
		} catch (Exception ex) {
			ex.printStackTrace();
			throw new RuntimeException(ex);
		} finally {
			IOUtils.closeQuietly(inputStream);
		}
	}

	public void openFile(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String name = req.getParameter("name");
		name = decode(name);
		ProcessProvider targetProvider = ProcessProviderUtils.getProcessProvider(name);
		if (targetProvider == null) {
			throw new RuntimeException("Unsupport file : " + name);
		}
		InputStream inputStream = targetProvider.loadProcess(name);
		try {
			byte[] bytes = IOUtils.toByteArray(inputStream);
			ProcessDefinition process = ProcessParser.parseProcess(bytes, 0, true);
			writeObjectToJson(resp, process);
		} catch (Exception ex) {
			ex.printStackTrace();
			throw new RuntimeException(ex);
		} finally {
			IOUtils.closeQuietly(inputStream);
		}
	}

	public void deleteFile(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String fileName = req.getParameter("fileName");
		fileName = decode(fileName);
		ProcessProvider provider = ProcessProviderUtils.getProcessProvider(fileName);
		provider.deleteProcess(fileName);
	}

	public void saveFile(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String fileName = req.getParameter("fileName");
		String content = req.getParameter("content");
		content = decode(content);
		ProcessProvider provider = ProcessProviderUtils.getProcessProvider(fileName);
		provider.saveProcess(fileName, content);
	}

	public void loadProcessProviders(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		List<ProcessProvider> providers = ProcessProviderUtils.getProviders();
		writeObjectToJson(resp, providers);
	}

	public void loadProcessProviderFiles(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		String providerName = req.getParameter("name");
		if (StringUtils.isBlank(providerName)) {
			throw new RuntimeException("Process provider name can not be null.");
		}
		ProcessProvider targetProcessProvider = ProcessProviderUtils.getProcessProviderByName(providerName);
		List<ProcessFile> files = targetProcessProvider.loadAllProcesses();
		writeObjectToJson(resp, files);
	}

	/**
	 * 打开本地流程图
	 * 
	 * @date 2023年7月
	 * @company www.bstek.com
	 * @param req
	 * @param resp
	 * @return
	 * @throws ServletException
	 * @throws IOException
	 */
	public void  openXmlFile(HttpServletRequest req, HttpServletResponse resp) throws IOException {
		InputStream inputStream = null;
		DiskFileItemFactory factory = new DiskFileItemFactory();
		req.getParameter("");
		ServletContext servletContext = req.getSession().getServletContext();
		File repository = (File) servletContext.getAttribute("javax.servlet.context.tempdir");
		factory.setRepository(repository);
		ServletFileUpload upload = new ServletFileUpload(factory);
		upload.setHeaderEncoding("utf-8");
		
		Map<String, Object> result = new HashMap<String, Object>();
		try {
			for (FileItem item : upload.parseRequest(req)) {
				if (item.getFieldName().equals("processFile")) {
					inputStream = item.getInputStream();
				}
			}
			if (inputStream != null) {
				ProcessDefinition process = defaultProcessDeployer.initDeployProcess(inputStream);
				result.put("result", "success");
				result.put("process", process);
			} else {
				throw new IllegalArgumentException("Can not found uflo process definition!");
			}
		} catch (Exception e) {
			//throw new ServletException(e);
			e.printStackTrace();
			result.put("result", "fail");
			result.put("error", e.getMessage());
		}
		
		ObjectMapper mapper = new ObjectMapper();
		resp.setContentType("text/json");
		resp.setCharacterEncoding("UTF-8");
		OutputStream out = resp.getOutputStream();
		try {
			mapper.writeValue(out, result);
		} finally {
			out.flush();
			out.close();
		}
	}

	/**
	 * 分页查询数据库表中流程图
	 * 
	 * @date 2023年7月
	 * @company www.bstek.com
	 * @param req
	 * @param resp
	 */
	public void loadProcessDefinition(HttpServletRequest req, HttpServletResponse resp) {
		try {
			Integer pageSize = Integer.valueOf(req.getParameter("pageSize"));
			Integer pageNum = Integer.valueOf(req.getParameter("pageNum"));
			String key = req.getParameter("key");
			String name = req.getParameter("name");
			// 查询到多少条为止
			int start = (pageNum - 1) * pageSize;
			Integer end = pageNum * pageSize;
			ProcessQuery pq = processService.loadProcessDefinition(start, end, key, name);
			List<ProcessDefinition> list = pq.list();
			int total = pq.count();
			PageData page = new PageData(list, pageSize, start, total);
			writeObjectToJson(resp, page);
		} catch (Exception ex) {
			ex.printStackTrace();
			throw new RuntimeException(ex);
		}

	}

	/**
	 * 获取数据库的流程实例及流程图构建
	 * 
	 * @date 2023年7月
	 * @param req
	 * @param resp
	 * @throws ServletException
	 * @throws IOException
	 */
	public void openUfloXml(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String processId = req.getParameter("processId");
		if (processId == null) {
			throw new RuntimeException("processId is null ");
		}
		try {
			ProcessDefinition process = commandService
					.executeCommand(new GetProcessCommand(Long.valueOf(processId), null, 0, null));
			writeObjectToJson(resp, process);
		} catch (Exception ex) {
			ex.printStackTrace();
			throw new RuntimeException(ex);
		}
	}

	/**
	 * 更新或者保存流程图 version不变
	 * 
	 * @date 2023年7月
	 * @param req
	 * @param resp
	 * @throws ServletException
	 * @throws IOException
	 */
	public void saveUfloXml(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
		try {
			String content = req.getParameter("content");
			Assert.notNull(content, "content is null");
			
			String processIds = req.getParameter("processId");
			ProcessDefinition pd = null;
			long processId = 0;
			int version=1;
			boolean flag=true;//默认为更新，即表中已有模板
			if (StringUtils.isNotEmpty(processIds)) {
				processId = Long.valueOf(processIds);
				pd = processService.getProcessById(Long.valueOf(processIds));
				if (pd == null) {
					processId = IDGenerator.getInstance().nextId();
					flag=false;
				}else {
					version=pd.getVersion();
					flag=true;
				}
			} else {
				//新发布
				flag=false;
				processId = IDGenerator.getInstance().nextId();
			}
			
			content = decode(content);
			// 判断数据路里面存在与否不去构建节点等对象信息
			// ProcessDefinition processByKey =
			// defaultProcessService.getProcessByKey(key,false);
			InputStream inputStream = IOUtils.toInputStream(content, "utf-8");
			
			ProcessDefinition pd2 = defaultProcessDeployer.deploy(inputStream,version, processId, flag,pd.getCreateDate());
			processService.putCacheProcessById(processId, pd2);
			Map<String, Object> result = new HashMap<String, Object>();
			try {
				result.put("result", "success");
				result.put("process", pd2);
			} catch (Exception ex) {
				ex.printStackTrace();
				result.put("result", "fail");
				result.put("error", ex.getMessage());

			}
			ObjectMapper mapper = new ObjectMapper();
			resp.setContentType("text/json");
			resp.setCharacterEncoding("UTF-8");
			OutputStream out = resp.getOutputStream();
			try {
				mapper.writeValue(out, result);
			} finally {
				out.flush();
				out.close();
			}
		} catch (RuntimeException ex) {
			ex.printStackTrace();
			throw new RuntimeException(ex);
		}

	}

	public void setCommandService(CommandService commandService) {
		this.commandService = commandService;
	}

	public void setProcessService(ProcessService processService) {
		this.processService = processService;
	}

	private String decode(String str) {
		if (str == null)
			return str;
		try {
			str = URLDecoder.decode(str, "utf-8");
			str = URLDecoder.decode(str, "utf-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return str;
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		super.setApplicationContext(applicationContext);
		DefaultProcessService defaultProcessService = applicationContext.getBean(DefaultProcessService.class);
		if (defaultProcessService != null) {
			this.defaultProcessService = defaultProcessService;
		}
		DefaultProcessDeployer defaultProcessDeployer = applicationContext.getBean(DefaultProcessDeployer.class);
		if (defaultProcessDeployer != null) {
			this.defaultProcessDeployer = defaultProcessDeployer;
		}
		Collection<AdditionalInfoProvider> coll = applicationContext.getBeansOfType(AdditionalInfoProvider.class)
				.values();
		if (coll.size() > 0) {
			additionalInfoProvider = coll.iterator().next();
		}
	}

	@Override
	public String url() {
		return "/designer";
	}
}
